package math.vector;

import math.Maths;

public class Vecmath 
{
    
    private Vecmath() 
    {
    }
    
    
    public static final vec3f ORIGIN = new vec3f(0, 0, 0);
    public static final vec3f AXIS_X = new vec3f(1, 0, 0);
    public static final vec3f AXIS_Y = new vec3f(0, 1, 0);
    public static final vec3f AXIS_Z = new vec3f(0, 0, 1);
    
    private static final float X_ANGLE_OFFSET = -Maths.PI_OVER_2;
    private static final float Y_ANGLE_OFFSET = -Maths.PI_OVER_2;
    private static final int X_ANGLE_DIR_FACTOR = 1;
    private static final int Y_ANGLE_DIR_FACTOR = -1;
    
    
    public static void calcLimitToLength(
            float xSource, float ySource, float zSource, 
            float lengthLimit, 
            vec3f dest)
    {
        float length = Vecmath.getLengthOf(xSource, ySource, zSource);
        if (length > lengthLimit)
        {
            Vecmath.calcNormalize(
                xSource, ySource, zSource, 
                dest
            );
            Vecmath.calcScale(
                lengthLimit, lengthLimit, lengthLimit, 
                dest
            );
        }
    }
    
    public static void calcTranslate(
            float xTrans, float yTrans, float zTrans, 
            vec3f dest)
    {
        dest.x += xTrans;
        dest.y += yTrans;
        dest.z += zTrans;
    }
    
    public static void calcScale(
            float xScale, float yScale, float zScale, 
            vec3f dest)
    {
        dest.x *= xScale;
        dest.y *= yScale;
        dest.z *= zScale;
    }
    
    public static void calcScaledCopyOf(
            float xSource, float ySource, float zSource, 
            float xScale, float yScale, float zScale, 
            vec3f dest)
    {
        dest.x = xSource * xScale;
        dest.y = ySource * yScale;
        dest.z = zSource * zScale;
    }
    
    public static void calcSetTo(
            float xSource, float ySource, float zSource, 
            vec3f dest)
    {
        dest.x = xSource;
        dest.y = ySource;
        dest.z = zSource;
    }
    
    public static void calcNormalize(
            float xSource, float ySource, float zSource, 
            vec3f dest)
    {
        dest.setTo(xSource, ySource, zSource);
        float rec_length = 1.0f / Vecmath.getLengthOf(dest);
        Vecmath.calcScale(
            rec_length, rec_length, rec_length, 
            dest
        );
    }
    
    public static void calcInvertedOf(
            float xSrc, float ySrc, float zSrc, 
            vec3f dest)
    {
        dest.x = -xSrc;
        dest.y = -ySrc;
        dest.z = -zSrc;
    }
    
    public static void calcSphericalFromDirection(
            float xEuc, float yEuc, float zEuc, 
            vec2f dest)
    {
        vec3f nEuclid = new vec3f();
        Vecmath.calcNormalize(
            xEuc, yEuc, zEuc, // source
            nEuclid // dest
        );
        float angleX = Maths.acosRad(nEuclid.y);
        float angleY = Maths.acosRad(nEuclid.x / Maths.sinRad(angleX));
//        float angleY = Maths.asinRad(-nEuclid.z / Maths.sinRad(angleX));
        dest.x = (angleX - X_ANGLE_OFFSET) * X_ANGLE_DIR_FACTOR;
        dest.y = (angleY - Y_ANGLE_OFFSET) * Y_ANGLE_DIR_FACTOR;
    }
    
    public static void calcDirectionFromSpherical(
            float xSpherical, float ySpherical, 
            vec3f dest)
    {
        vec2f rotsCos = new vec2f();
        rotsCos.x = Maths.cosRad(X_ANGLE_DIR_FACTOR * xSpherical + X_ANGLE_OFFSET);
        rotsCos.y = Maths.cosRad(Y_ANGLE_DIR_FACTOR * ySpherical + Y_ANGLE_OFFSET);
        
        vec2f rotsSin = new vec2f();
        rotsSin.x = Maths.sinRad(X_ANGLE_DIR_FACTOR * xSpherical + X_ANGLE_OFFSET);
        rotsSin.y = Maths.sinRad(Y_ANGLE_DIR_FACTOR * ySpherical + Y_ANGLE_OFFSET);
        
        dest.x = rotsSin.x * rotsCos.y;
        dest.y = rotsCos.x;
        dest.z = -rotsSin.x * rotsSin.y;
    }
    
    public static void calcCrossProduct(
            float x0, float y0, float z0, 
            float x1, float y1, float z1, 
            vec3f dest)
    {
        dest.x = (y0 * z1) - (z0 * y1);
        dest.y = (z0 * x1) - (x0 * z1);
        dest.z = (x0 * y1) - (y0 * x1);
    }
    
    public static void calcDifferenceOf(
            vec3f one, 
            vec3f two, 
            vec3f dest)
    {
        dest.x = one.x - two.x;
        dest.y = one.y - two.y;
        dest.z = one.z - two.z;
    }
    
    public static void calcRotationOf(
            float xSource, float ySource, float zSource, 
            float xAxis, float yAxis, float zAxis, 
            float angleRad, 
            vec3f dest)
    {
        float p = xAxis * Maths.sinRad(angleRad * 0.5f);
        float q = yAxis * Maths.sinRad(angleRad * 0.5f);
        float r = zAxis * Maths.sinRad(angleRad * 0.5f);
        float s = Maths.cosRad(angleRad * 0.5f);
        float m00 = 1 - 2 * q * q - 2 * r * r; // m00  m01  m02
        float m01 = 2 * p * q - 2 * s * r;     // m10  m11  m12
        float m02 = 2 * p * r + 2 * s * q;     // m20  m21  m22
        float m10 = 2 * p * q + 2 * s * r;
        float m11 = 1 - 2 * p * p - 2 * r * r;
        float m12 = 2 * q * r + 2 * s * p;
        float m20 = 2 * p * r - 2 * s * q;
        float m21 = 2 * q * r - 2 * s * p;
        float m22 = 1 - 2 * p * p - 2 * q * q;
        dest.x = xSource * m00 + ySource * m01 + zSource * m02;
        dest.y = xSource * m10 + ySource * m11 + zSource * m12;
        dest.z = xSource * m20 + ySource * m21 + zSource * m22;
    }
    
    
    
    public static boolean getEquals(vec3f one, vec3f two)
    {
        return (
            one.x == two.x && 
            one.y == two.y && 
            one.z == two.z
        );
    }
    
    public static float getDotProductOf(vec3f source0, vec3f source1)
    {
        float xProduct = (source0.x * source1.x);
        float yProduct = (source0.y * source1.y);
        float zProduct = (source0.z * source1.z);
        float dotProduct = (xProduct + yProduct + zProduct);
        return dotProduct;
    }
    
    public static float getLengthOf(
            float xSource, 
            float ySource, 
            float zSource)
    {
        float x2 = Maths.sq(xSource);
        float y2 = Maths.sq(ySource);
        float z2 = Maths.sq(zSource);
        float length = Maths.sqrt(x2 + y2 + z2);
        return length;
    }
    
    public static float getLengthOf(vec3f source)
    {
        return Vecmath.getLengthOf(source.x, source.y, source.z);
    }
    
    public static float getLengthSqOf(
            float xSource, 
            float ySource, 
            float zSource)
    {
        float x2 = Maths.sq(xSource);
        float y2 = Maths.sq(ySource);
        float z2 = Maths.sq(zSource);
        float lengthSq = (x2 + y2 + z2);
        return lengthSq;
    }
    
    public static float getLengthSqOf(vec3f source)
    {
        return Vecmath.getLengthSqOf(source.x, source.y, source.z);
    }
    
    public static vec2f getSphericalFromDirection(float xDir, float yDir, float zDir)
    {
        vec2f spherical = new vec2f();
        Vecmath.calcSphericalFromDirection(
            xDir, yDir, zDir, // source
            spherical // dest
        );
        return spherical;
    }
    
    public static vec3f getDirectionFromSpherical(float xSpherical, float ySpherical)
    {
        vec3f nEuclid = new vec3f();
        Vecmath.calcDirectionFromSpherical(
            xSpherical, ySpherical, 
            nEuclid
        );
        return nEuclid;
    }
    
    public static vec3f getNormalizedOf(vec3f vector)
    {
        vec3f normalized = new vec3f();
        Vecmath.calcNormalize(
            vector.x, vector.y, vector.z, // source
            normalized // dest
        );
        return normalized;
    }
    
    public static vec3f getCrossProductOf(vec3f one, vec3f two)
    {
        vec3f crossProduct = new vec3f();
        Vecmath.calcCrossProduct(
            one.x, one.y, one.z, 
            two.x, two.y, two.z, 
            crossProduct // dest
        );
        return crossProduct;
    }
    
    public static vec3f getDifferenceOf(
            vec3f pt0, 
            vec3f pt1)
    {
        vec3f difference = new vec3f();
        Vecmath.calcDifferenceOf(
            pt0, pt1, // a - b
            difference // dest
        );
        return difference;
    }
    
    public static vec3f getSurfaceNormalOf(
            vec3f pt0, 
            vec3f pt1, 
            vec3f pt2)
    {
        vec3f dir0 = Vecmath.getDifferenceOf(pt2, pt0);
        vec3f dir1 = Vecmath.getDifferenceOf(pt2, pt1);
        vec3f crossProduct = Vecmath.getCrossProductOf(dir0, dir1);
        vec3f surfaceNormal = Vecmath.getNormalizedOf(crossProduct);
        return surfaceNormal;
    }
    
    public static vec3f getInvertedOf(vec3f vector)
    {
        vec3f inverted = new vec3f();
        Vecmath.calcInvertedOf(
            vector.x, vector.y, vector.z, 
            inverted
        );
        return inverted;
    }
    
    public static vec3f getRotationOf(
            vec3f vector, 
            vec3f axis, 
            float angleRad)
    {
        vec3f rotatedVector = new vec3f();
        Vecmath.calcRotationOf(
            vector.x, vector.y, vector.z, 
            axis.x, axis.y, axis.z, 
            angleRad, 
            rotatedVector
        );
        return rotatedVector;
    }
    
}
